package _05_linkedMatrix;

import _02_seqList.SeqList;
import _02_singlyLinkedList.Node;
import _02_sortedSinglyLinkedList.SortedSinglyLinkedList;
import _05_triple.Triple;

// 稀疏矩阵行的单链表类
public class LinkedMatrix {
    
    // 矩阵行数、列数
    public int rows, columns;
    // 行指针顺序表，元素是排序单链表
    public SeqList<SortedSinglyLinkedList<Triple>> rowlist;
    
    // 构造m x n矩阵，元素为0，若m < 0或n < 0，则参数错，抛出无效参数异常
    public LinkedMatrix(int m, int n) {
        if (m >= 0 && n >= 0) {
            this.rows = m;
            this.columns = n;
            this.rowlist = new SeqList<SortedSinglyLinkedList<Triple>>(m);
            // 顺序表内元素为null，先创空单链表
            for (int i = 0; i < m; i++) {
                this.rowlist.insert(i, new SortedSinglyLinkedList<Triple>());
            }
        } else {
            throw new IllegalArgumentException("矩阵行列数不能 < 0, m = " + m + ", n = " + n);
        }
    }
    
    // 构造n x n矩阵，元素为0
    public LinkedMatrix(int n) {
        this(n, n);
    }
    
    // 构造0 x 0矩阵
    public LinkedMatrix() {
        this(0, 0);
    }
    
    // 构造m x n矩阵，有tris三元组数据提供元素值
    public LinkedMatrix(int m, int n, Triple[] tris) {
        this(m, n);
        for (int i = 0; i < tris.length; i++) {
            this.set(tris[i]);
        }
    }
    
    // 构造m x n矩阵，tris字符串指定三元组形式的元素序列，","分割，没有空格
    public LinkedMatrix(int m, int n, String tris) {
        this(m, n);
        int start = 0, end = 0;
        while (start < tris.length() && (end = tris.indexOf(')', start)) != -1) {
            this.set(new Triple(tris.substring(start, end+1)));
            start = end + 2;
        }
    }
    
    // 设置矩阵第i行第j列元素为x，查找、插入、删除算法均比较三元组大小
    public void set(int i, int j, int x) {
        // i, j越界处理
        if (i >= 0 && i < this.rows && j >= 0 && j < this.columns) {
            // 不能直接就插入，因为不确定第i行是否存在j相同的元素，存在的话再添加就重复了
            SortedSinglyLinkedList<Triple> link = this.rowlist.get(i);
            Triple tri = new Triple(i, j, x) {
                @Override
                public boolean equals(Object obj) {
                    if (this == obj) {
                        return true;
                    }
                    if (obj instanceof Triple) {
                        Triple triple = (Triple) obj;
                        return this.getRow() == triple.getRow() && this.getColumn() == triple.getColumn() ? true : false;
                    }
                    return false;
                }
            };
            // 若查找成功，则删除(i, j, ?)结点，查找不成功，则没有该结点，不添加，不做处理
            if (x == 0) {
                link.remove(tri);
            } else {
                Node<Triple> find = link.search(tri);
                if (find != null) {
                    find.data.setValue(x);
                } else {
                    link.insert(tri);
                }
            }
        } else {
            throw new IndexOutOfBoundsException("i = " + i + ", j = " + j);
        }
    }
    
    // 以三元组tri设置矩阵元素
    public void set(Triple tri) {
        this.set(tri.getRow(), tri.getColumn(), tri.getValue());
    }
    
    // 返回矩阵第i行第j列元素，若i、j序号越界，则抛出序号越界异常
    public int get(int i, int j) {
        if (i >= 0 && i < this.rows && j >= 0 && j < this.columns) {
            Triple tri = new Triple(i, j, 0) {
                @Override
                public boolean equals(Object obj) {
                    if (this == obj) {
                        return true;
                    }
                    if (obj instanceof Triple) {
                        Triple triple = (Triple) obj;
                        return this.getRow() == triple.getRow() && this.getColumn() == triple.getColumn() ? true : false;
                    }
                    return false;
                }
            };
            Node<Triple> find = this.rowlist.get(i).search(tri);
            return (find != null) ? find.data.getValue() : 0;
        }
        return 0;
    }
    
    // 返回矩阵的字符串描述
    public String toString() {
        String str = "";
        //循环次数为行指针顺序表长度
        for (int i = 0; i < this.rows; i++) {
            str += i + " -> " + this.rowlist.get(i).toString() + "\n";
        }
        //获得第i行排序单链表的描述字符串
        return str;
    }
    
    // 输出矩阵
    public void printMatrix() {
        System.out.println("矩阵" + this.getClass().getName() + "(" + rows + "x" + columns + ")：");
        for (int i = 0; i < this.rows; i++) {
            Node<Triple> p = this.rowlist.get(i).head.next;
            for (int j = 0; j < this.columns; j++) {
                if (p != null && j == p.data.getColumn()) {
                    System.out.print(String.format("%4d", p.data.getValue()));
                    p = p.next;
                } else {
                    System.out.print(String.format("%4d", 0));
                }
            }
            System.out.println();
        }
    }
    
    // 设置矩阵为m行n列，若m指定行数较大，则将行指针顺序表扩容，使用原各行单链表
    public void setRowsColumns(int m, int n) {
        if (m >= 0 && n >= 0) {
            if (m > this.rows) {
                for (int i = this.rows-1; i < m; i++) {
                    // 调用顺序表insert方法，自动扩容
                    this.rowlist.insert(new SortedSinglyLinkedList<Triple>());
                }
            }
            this.rows = m;
            this.columns = n;
        } else {
            throw new IndexOutOfBoundsException("矩阵行列数不能小于0，m = " + m + ", n = " + n);
        }
    }
    
    // 比较this与obj矩阵是否相等
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj instanceof LinkedMatrix) {
            LinkedMatrix link = (LinkedMatrix)obj;
            return this.rows == link.rows && this.columns == link.columns && link.rowlist.equals(this.rowlist);
        }
        return false;
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println("测试构造方法(int m, int n, String tris)：");
        LinkedMatrix lm = new LinkedMatrix(4, 6, "(0,0,1),(0,0,2),(0,1,20),(0,1,0),(2,1,6),(2,3,10),(3,5,7)");
        System.out.println(lm);
        System.out.println("测试get(int i, int j)：");
        System.out.println(lm.get(0, 0));
        System.out.println("测试printMatrix()：");
        lm.printMatrix();
    }
}
